#include "Rodeur.h"

#include <iomanip>

#include "lib.h"

#include "ATL.h"

#include <boost/assign/list_of.hpp>

using namespace std;
using namespace boost::assign;

const unsigned Rodeur::nbrCompetences = 16;

const unsigned Rodeur::nbrRestrictions = 7;

const unsigned Rodeur::ppAlloues = 60;

const vector<CompetenceClasse> Rodeur::Comp = list_of
  (CompetenceClasse("Combat ambidextre"               ,  5))
  (CompetenceClasse("Déplacement silencieux"          ,  5, Pourcentage))
  (CompetenceClasse("Détecter les bruits"             , 10))
  (CompetenceClasse("Empathie animale"                , 10))
  (CompetenceClasse("Ennemi juré"                     , 10))
  (CompetenceClasse("Grimper"                         , 10, Pourcentage))
  (CompetenceClasse("Langage des animaux"             ,  5))
  (CompetenceClasse("Neutralisation"                  , 10))
  (CompetenceClasse("Passage sans trace"              , 10))
  (CompetenceClasse("Pistage"                         ,  5))
  (CompetenceClasse("Se cacher dans l'ombre"          ,  5, Pourcentage))
  (CompetenceClasse("Sorts cléricaux"                 , 10))
  (CompetenceClasse("Spécialisation"                  , 10))
  (CompetenceClasse("Suivants"                        , 10))
  (CompetenceClasse("Talent pour les arcs"            ,  5))
  (CompetenceClasse("Trouver et désamorcer les pièges", 10));

const vector<CompetenceClasse> Rodeur::Restric = list_of
  (CompetenceClasse("Limitation d'armes"                          ,  5))
  (CompetenceClasse("Limitation à la maille"                      ,  5))
  (CompetenceClasse("Limitation au cuir clouté"                   , 10))
  (CompetenceClasse("Sans armure"                                 , 15))
  (CompetenceClasse("Lim : potions, huiles et parchemins"         ,  5))
  (CompetenceClasse("Lim : anneau, de bâton, bâtonnet et baguette",  5))
  (CompetenceClasse("Lim : armes et armures magiques"             ,  5));

const vector< Prerequis > Rodeur::Limites = list_of
  (Prerequis(0, 13))
  (Prerequis(1, 13))
  (Prerequis(2, 14))
  (Prerequis(4, 14));

const set< string > Rodeur::RacesAdmises = list_of
  ( "Humain" ) ( "Elfe" ) ( "Demi-Elfe" );

/** Constructeur usuel.
 * Permet la construction d'un paladin de A à Z, càd on détermine ici
 * les restrictions, puis les compétences.
 *
 * @param[in] xp le nombre de pts d'expérience du Rôdeur.
 * @param[in] trait les traits du Rôdeur, le 7ème contient le score de
 * force exceptionnel, fixé à 0 si non applicable.
 * @param[in,out] PP nombre de points de personnage (PP) à disposition
 * pour la création du Rôdeur.
 * @param[in] race nom de la race du personnage.
 */
Rodeur::Rodeur(unsigned xp, const vector< unsigned >& trait,
	       unsigned& PP, const string& race)
  : Classe( xp, Combattants, "Rôdeur" )
{
  //NomClasse = "Rôdeur";
  NombrePPsAlloues = ppAlloues;
  PP += NombrePPsAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  blank() << setfill('*') << setw(56) << '*' << endmsg;
  blank() << "Création d'un rôdeur" << endmsg << endmsg;

  construitClasse( PP, trait, race );

  blank() << endmsg << setfill('*') << setw(56) << '*' << endmsg;
  calculNiveau();
  calculNivMag();
}

/** Constructeur simplifié.
 * Permet de construire un Rôdeur lors de la lecture d'un ficier xml.
 *
 * @param[in] xp le nombre de pts d'expérience du Rôdeur.
 * @param[in] trait les traits du Rôdeur, le 7ème contient le score de
 * force exceptionnel, fixé à 0 si non applicable.
 * @param[in] race nom de la race du personnage.
 */
Rodeur::Rodeur( unsigned xp )
  : Classe( xp, Combattants, "Rôdeur" )
{
  //NomClasse = "Rôdeur";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
  calculNivMag();
}

/** Constructeur pour le mode graphique.
 * Permet de construire une instance de Mage avec le contenu
 * minimal, le nombre de points d'expériences et les capacités étant
 * déterminées après la déclaration.
 *
 * @param trait les traits du Mage, le 7ème contient le score de
 * force exceptionnel, fixé à 0 si non applicable.
 * @param[in] UI contrôle, est présent pour des raisons historiques.
 * @param[in] race nom de la race du personnage.
 */
Rodeur::Rodeur( const bool& UI )
  : Classe( 0, Combattants, "Rôdeur" )
{
  if (!UI)
    {
      error() << "Création du personnage impossible" << endmsg;
      exit(1);
    }

  //NomClasse = "Rôdeur";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
  calculNivMag();
}

/**
 * Constructeur de "copie", destiné à être utilisé depuis MetaClasse.
 *
 * @param[in] ptr pointeur sur un objet de type Classe (MetaClasse en
 * fait).
 */
Rodeur::Rodeur(Classe *ptr)
  : Classe( *ptr )
{
  NomClasse = "Rôdeur";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
  calculNivMag();
}

/** 
 * Destructeur, ne fait rien.
 */
Rodeur::~Rodeur()
{}

void Rodeur::calculNiveau()
{
  if (XP < 75000)
    {
      unsigned i;
      for (i = 0; i < 5; i++)
	{
	  if (XP < (unsigned) 2250 * puissance(2, i))
	    break;
	}
      Niveau = i + 1;
    }
  else if (XP < 150000)
    Niveau = 7;
  else if (XP < 300000)
    Niveau = 8;
  else
    Niveau = (XP - 300000) / 300000 + 9;
}

void Rodeur::js(unsigned *tab) const
{
  *(tab + 0) = 14; // Paralysie, Poison ou Mort magique
  *(tab + 1) = 16; // Bâton, batonnet baguette
  *(tab + 2) = 15; // Pétrification ou Métamorphose
  *(tab + 3) = 17; // Souffles
  *(tab + 4) = 17; // Sorts
  
  unsigned cat;
  if (Niveau <= 2)
    cat = 0;
  else  if (Niveau <= 4)
    cat = 1;
  else  if (Niveau <= 6)
    cat = 3;
  else  if (Niveau <= 8)
    cat = 4;
  else  if (Niveau <= 10)
    cat = 6;
  else  if (Niveau <= 12)
    cat = 7;
  else  if (Niveau <= 14)
    cat = 9;
  else  if (Niveau <= 16)
    cat = 10;
  else
    cat = 11;

  *(tab + 0) -= cat;
  *(tab + 1) -= cat;
  *(tab + 2) -= cat;
  *(tab + 3) -= cat;
  *(tab + 4) -= cat;

  if (cat == 3)
    *(tab + 3) = 13;
  else if (cat == 4)
    *(tab + 3) = 12;
  else if (cat == 4)
    *(tab + 3) = 9;
  else if (cat == 6)
    *(tab + 3) = 8;
  else if (cat == 7)
    *(tab + 3) = 5;
  else if (cat >= 10)
    *(tab + 3) = 4;
}

unsigned Rodeur::calculeTAc0() const
{
  return 21 - Niveau;
}

/**
 * Le Rôdeur aquiert des sorts de prêtre au niveau 9, s'il possède la
 * compétence.
 */
void Rodeur::calculNivMag()
{
  if (Niveau < 8)
    NiveauMagique = 0;
  else if (Niveau < 14)
    NiveauMagique = (Niveau - 7) / 2;
  else
    NiveauMagique = 3;
}

/**
 * Le Rôdeur peut prende la compétence "Sorts cléricaux", qui lui donne
 * accès dès le niveau 9 aux sphères Animale et Végétale.
 */
void Rodeur::ecrireSorts( const string& nom_fichier, const bool& ecrase,
			  const unsigned& ) const
{
  bool aDesSorts( false );
  if ( find( Competences.begin(), Competences.end(), 9 ) != Competences.end() )
    aDesSorts = true;

  /*
   * Ajouter le nombre de sorts et un en-tête
   */

  ofstream sortie;
  if (ecrase)
    sortie.open( nom_fichier.c_str() );
  else
    sortie.open( nom_fichier.c_str(), ios::app );
  if ( aDesSorts )
    {
      unsigned i;
      for ( i = 1; i <= NiveauMagique; i++ )
	sortie << "\\input{Sorts_clerc/Animale_" << ecrireString(i)
	       << ".inc.tex}" << endmsg;
      for ( i = 1; i <= NiveauMagique; i++ )
	sortie << "\\input{Sorts_clerc/Végétale_" << ecrireString(i)
	       << ".inc.tex}" << endmsg;
    }
  else
    sortie << "% Rodeur sans sorts" << endmsg;
  sortie.close();
}

void Rodeur::sortsParNiveau( vector< unsigned >& sorts,
			     const unsigned& ) const
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Rodeur::nbrSort -> début" << endmsg;

  vector< unsigned > tmp( 3, 0 );
  
  switch( Niveau )
    {
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
      break;
    case 8:
      tmp[0] = 1;
      break;
    case 9:
      tmp[0] = 2;
      break;
    case 10:
      tmp[0] = 2;
      tmp[1] = 1;
      break;
    case 11:
      tmp[0] = 2;
      tmp[1] = 2;
      break;
    case 12:
      tmp[0] = 2;
      tmp[1] = 2;
      tmp[2] = 1;
      break;
    case 13:
      tmp[0] = 3;
      tmp[1] = 2;
      tmp[2] = 1;
      break;
    case 14:
      tmp[0] = 3;
      tmp[1] = 2;
      tmp[2] = 2;
      break;
    case 15:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 2;
      break;
    default:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 3;
    }

  sorts.assign( tmp.begin(), tmp.end() );
  sorts.resize( NiveauMagique );
  
  func() << "Rodeur::nbrSort -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Permet de gérer les compétences martiales, càd lesquelles sont accessibles
 * à quel coût.
 *
 * Pour un Rôdeur, comme pour tous les combattants, le coût est de 3 pour
 * n'importe quelle arme. De plus, il peut acquérir un groupe restreint pour
 * 4 pp et un groupe large pour 5 pp.
 */
void Rodeur::prixAcces(map<string, unsigned>*& Map) const
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Rodeur::prixAcces -> début" << endmsg;

  map<string, unsigned>::const_iterator mapIt, mapBegin(Map -> begin()),
    mapEnd(Map -> end());

  for (mapIt = mapBegin; mapIt != mapEnd; mapIt++)
    {
      lfunc() << mapIt -> first << endmsg;
    }
  Map -> operator[]("Rôdeur") = 2;
  
  func() << "Rodeur::prixAcces -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

string Rodeur::scoreAdditionnel( const unsigned& i,
				 const vector< unsigned >& traits ) const
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Rodeur::scoreAdditionnel -> début" << endmsg;

  string renvoi( "" );
  unsigned resultat( 0 );

  if ( Comp[ i ].nom() == "Déplacement silencieux" ||
       Comp[ i ] .nom() == "Trouver et désamorcer les pièges" )
    {
      switch( Niveau )
	{
	case 1:
	  resultat = 10;
	  break;
	case 2:
	  resultat = 15;
	  break;
	case 3:
	  resultat = 20;
	  break;
	case 4:
	  resultat = 25;
	  break;
	case 5:
	  resultat = 31;
	  break;
	case 6:
	  resultat = 37;
	  break;
	case 7:
	  resultat = 43;
	  break;
	case 8:
	  resultat = 49;
	  break;
	case 9:
	  resultat = 56;
	  break;
	case 10:
	  resultat = 63;
	  break;
	case 11:
	  resultat = 70;
	  break;
	case 12:
	  resultat = 77;
	  break;
	case 13:
	  resultat = 85;
	  break;
	case 14:
	  resultat = 93;
	  break;
	default:
	  resultat = 99;
	}
    }
  else if (  Comp[ i ].nom() == "Détecter les bruits" ||
	     Comp[ i ].nom() == "Grimper" ||
	     Comp[ i ].nom() == "Se cacher dans l'ombre" )
    {
      switch( Niveau )
	{
	case 1:
	  resultat = 15;
	  break;
	case 2:
	  resultat = 21;
	  break;
	case 3:
	  resultat = 27;
	  break;
	case 4:
	  resultat = 33;
	  break;
	case 5:
	  resultat = 40;
	  break;
	case 6:
	  resultat = 47;
	  break;
	case 7:
	  resultat = 55;
	  break;
	case 8:
	  resultat = 62;
	  break;
	case 9:
	  resultat = 70;
	  break;
	case 10:
	  resultat = 78;
	  break;
	case 11:
	  resultat = 86;
	  break;
	case 12:
	  resultat = 94;
	  break;
	default:
	  resultat = 99;
	}
      if ( Comp[ i ].nom() ==  "Détecter les bruits" )
	resultat += traits[ 4 ];
      else if ( Comp[ i ].nom() ==  "Grimper" )
	resultat += traits[ 1 ];
    }
  else if (  Comp[ i ].nom() == "Neutralisation" )
    {
      switch( Niveau )
	{
	case 1:
	case 2:
	case 3:
	case 4:
	  renvoi = "dégâts x2";
	  break;
	case 5:
	case 6:
	case 7:
	case 8:
	  renvoi = "dégâts x3";
	  break;
	case 9:
	case 10:
	case 11:
	case 12:
	  renvoi = "dégâts x4";
	  break;
	default:
	  renvoi = "dégâts x5";
	}
    }

  if ( resultat > 99 )
    resultat = 99;
	    
  if ( renvoi == "" )
    renvoi = ecrireString( resultat );
	    
  func() << "Rodeur::scoreAdditionnel -> fin" << endmsg;
  /*Debug::*/depileNiveau();

  return renvoi;
}

unsigned Rodeur::aSpecialisation() const
{
  return find( Competences.begin(), Competences.end(), 12 ) !=
    Competences.end() ? 1 : 0;
}
